package com.community.util.wechat;

import com.alibaba.fastjson.JSONObject;
import com.community.bean.TextMessage;
import com.community.util.HttpRequestUtil;
import com.community.util.MessageUtil;
import com.community.util.SpringUtil;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.dom4j.Element;
import org.dom4j.io.DOMReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;

@Service
public class WechatService {
    private static final Logger LOGGER = LoggerFactory.getLogger(WechatService.class);

    public static final String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    public static final String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    /**
     * 获取JS页面的ticket
     */
    public final static String jsticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";

    public final static String web_access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

    public final static String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    public final static String NOTIFY_PATH = "/wx/wxPayNotify";

    @Value("${wx.appid}")
    public String appid;

    @Value("${wx.secret}")
    private String secret;

    @Value("${wx.token}")
    private String token;

    @Value("${wx.merchantKey}")
    public String merchantKey;

    @Value("${wx.merchantId}")
    public String merchantId;

    @Value("${wx.baseUrl}")
    public String baseUrl;

    @Value("${wx.welcome}")
    public String welcome;

    @Value("${wx.mediaId}")
    public String mediaId;


    public static final ExpiryMap expiryMap = new ExpiryMap();

    public String wxAuth(WeChatAuth wc){
        if(wc.checkSignature(token)){
            // 通过检验signature对请求进行校验，若校验成功则原样返回echostr，表示接入成功，否则接入失败
            return wc.getEchostr();
        }else{
            return "error";
        }
    }

    public AccessToken getAccessToken() {
        AccessToken accessToken = (AccessToken)expiryMap.get("accessToken");
        if(accessToken==null){
            String requestUrl = access_token_url.replace("APPID" , appid).replace("APPSECRET" , secret);
            String resp = HttpRequestUtil.sendGet(requestUrl);
            accessToken = JSONObject.parseObject(resp,AccessToken.class);
            expiryMap.put("accessToken",accessToken,(accessToken.getExpires_in()-10)*1000l);
        }
        return accessToken;
    }

    //通过code换取网页授权access_token
    public WebAccessToken getWebToken(String code) {
        WebAccessToken webToken = (WebAccessToken)expiryMap.get(code);
        if(webToken ==null){
            // 拼装token的url
            String requestUrl = web_access_token_url.replace("APPID", appid).replace("SECRET", secret).replace("CODE", code);
            // 获取 token
            String resp = HttpRequestUtil.sendGet(requestUrl);
            webToken = JSONObject.parseObject(resp,WebAccessToken.class);
            if(webToken.getOpenid()!=null){
                expiryMap.put(code,webToken,(webToken.getExpires_in()-10)*1000l);
            }
        }
        return webToken;
    }

    public JsTicket getJsTicket() {
        JsTicket jsTicket = (JsTicket)expiryMap.get("jsTicket");
        if(jsTicket == null){
            AccessToken accessToken = getAccessToken();
            String requestUrl = jsticket_url.replace("ACCESS_TOKEN" , accessToken.getAccess_token());
            String resp = HttpRequestUtil.sendGet(requestUrl);
            jsTicket = JSONObject.parseObject(resp,JsTicket.class);
            expiryMap.put("jsTicket",jsTicket,(jsTicket.getExpires_in()-10)*1000l);
        }
        return jsTicket;
    }

    public String createMenu(){
        String resp = "";
        try {
            byte[] bytes = Files.readAllBytes(Paths.get(SpringUtil.webRoot.getAbsolutePath()+"/menu.json"));
            String text = new String(bytes).replaceAll("\\{appid\\}", appid)
                .replaceAll("\\{mediaId\\}", mediaId).replaceAll("\\{baseUrl\\}", baseUrl);
            JSONObject menu = JSONObject.parseObject(text, JSONObject.class);
            AccessToken accessToken = getAccessToken();
            String requestUrl = menu_create_url.replace("ACCESS_TOKEN" , accessToken.getAccess_token());
            resp = HttpRequestUtil.sendPostJson(requestUrl,menu.toJSONString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resp;
    }

    
    /**
     * 处理微信发来的请求
     * 
     * @param request
     * @return
     */
    public String weixinPost(HttpServletRequest request) {
        String respMessage = null;
        try {

            // xml请求解析
            Map<String, String> requestMap = MessageUtil.xmlToMap(request);

            // 发送方帐号（open_id）
            String fromUserName = requestMap.get("FromUserName");
            // 公众帐号
            String toUserName = requestMap.get("ToUserName");
            // 消息类型
            String msgType = requestMap.get("MsgType");
            // 消息内容
            String content = requestMap.get("Content");
            
            System.out.println("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType);
            
            // 文本消息
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
            	
            	//这里根据关键字执行相应的逻辑，只有你想不到的，没有做不到的
                if(content.equals("xxx")){
                    
                }
                
                //自动回复
                TextMessage text = new TextMessage();
                text.setContent("the text is" + content);
                text.setToUserName(fromUserName);
                text.setFromUserName(toUserName);
                text.setCreateTime(new Date().getTime() + "");
                text.setMsgType(msgType);
                
                respMessage = MessageUtil.textMessageToXml(text);
               
            } 
            // 事件推送
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
                String eventType = requestMap.get("Event");// 事件类型
                System.out.println("eventType="+eventType);
                
                // 订阅
                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
                    
                    TextMessage text = new TextMessage();
                    text.setContent(welcome);
                    text.setToUserName(fromUserName);
                    text.setFromUserName(toUserName);
                    text.setCreateTime(new Date().getTime() + "");
                    text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
                    
                    respMessage = MessageUtil.textMessageToXml(text);
                } 
                // TODO 取消订阅后用户再收不到公众号发送的消息，因此不需要回复消息
                else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// 取消订阅
                    
                    
                } 
                else if (eventType.equals(MessageUtil.EVENT_TYPE_VIEW)) {// view
                	
                } 
                // 自定义菜单点击事件
                else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
                    String eventKey = requestMap.get("EventKey");// 事件KEY值，与创建自定义菜单时指定的KEY值对应
                    if (eventKey.equals("contact_cs")) {
                        TextMessage text = new TextMessage();
                        text.setContent("金榜QQ群：551041680 \n 客服热线：0579-82380999  \n 资深专家面对面服务预约热线：18905792351（吴老师）");
                        text.setToUserName(fromUserName);
                        text.setFromUserName(toUserName);
                        text.setCreateTime(new Date().getTime() + "");
                        text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
                        
                        respMessage = MessageUtil.textMessageToXml(text);
                    }
                }
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return respMessage;
    }

    //获取预支付信息
    public Map<String, String> getPrepayInfo(String openid,String body,String out_trade_no,String total_fee,String ip) throws Exception {
        // 定义SortedMap存储获取预支付信息的参数
        SortedMap<String, String> paramsMap = new TreeMap<String, String>();
        // 设置paramsMap
        paramsMap.put("appid", appid);
        paramsMap.put("openid", openid);
        paramsMap.put("mch_id", merchantId);
        paramsMap.put("nonce_str", getNonceStr());
        paramsMap.put("body", body);
        paramsMap.put("out_trade_no", out_trade_no);
        paramsMap.put("total_fee", total_fee);
        paramsMap.put("spbill_create_ip", ip);
        paramsMap.put("notify_url", baseUrl +NOTIFY_PATH);
        paramsMap.put("trade_type", "JSAPI");
        // 获取签名
        paramsMap.put("sign", getSign(paramsMap));
        // 获取预支付单 参数
        String prePayParams = mapToXML(paramsMap);
        // 统一下单接口
        URL orderUrl = new URL(unifiedorder_url);
        // 定义HttpURLConnection
        HttpURLConnection conn = null;
        // 定义BufferedReader
        BufferedReader reader = null;
        // 定义OutputStreamWriter
        OutputStreamWriter out = null;
        // 定义StringBuffer存储读取结果
        StringBuffer prePayStr = new StringBuffer();
        try {
            // 打开连接
            conn = (HttpURLConnection) orderUrl.openConnection();
            // 设置连接主机超时（单位：毫秒)
            conn.setConnectTimeout(10000);
            // 设置从主机读取数据超时（单位：毫秒)
            conn.setReadTimeout(10000);
            // post请求参数要放在http正文内，设置成true，默认是false
            conn.setDoOutput(true);
            // 设置是否从httpUrlConnection读入，默认情况下是true
            conn.setDoInput(true);
            // Post 请求不能使用缓存
            conn.setUseCaches(false);
            // 设定传送的内容类型是可序列化的java对象(如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)
            // conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
            conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
            // 设定请求的方法为"POST"，默认是GET
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Length", String.valueOf(prePayParams.length()));
            LOGGER.info(prePayParams);
            // 字节流字符流转换器
            out = new OutputStreamWriter(conn.getOutputStream(), "utf-8");
            // 发送请求参数
            out.write(prePayParams);
            out.flush();
            // 获取输入流
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            // 定义String存储读取的每行数据
            String str = "";
            // 从输入流中读取相应的内容
            while ((str = reader.readLine()) != null) {
                prePayStr.append(str);
            }
            LOGGER.error(conn.getResponseCode()+":"+prePayStr.toString());
            // 如果返回码不正确，返回空
            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
                throw new Exception("获取预支付单异常");
            }
        } catch (Exception e) {
            throw e;
        } finally {
            // 关闭输出流
            if (out != null) {
                out.close();
            }
            // 关闭输入流
            if (reader != null) {
                reader.close();
            }
            // 关闭连接
            if (conn != null) {
                conn.disconnect();
            }
        }
        // 返回结果
        return XMLToMap(new ByteArrayInputStream(prePayStr.toString().getBytes("UTF-8")));
    }

    //生成随机字符串
    public static String getNonceStr() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    //生成时间戳
    public static String createTimestamp() {
        return Long.toString( System.currentTimeMillis() / 1000 );
    }

    public  String getOrderIdByTime() {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String newDate=sdf.format(new Date());
        return newDate+ (Math.random()+"").substring(2,5);
    }

    //签名
    public String getSign(SortedMap<String, String> params) throws Exception {
        // 定义StringBuffer
        StringBuffer sb = new StringBuffer();
        // 遍历map，拼接生成签名所需字符串
        for (String paramKey : params.keySet()) {
            // 获取对应的参数值
            String paramValue = params.get(paramKey);
            // 判断键是否为sign,值是否为空
            if (!"sign".equals(paramKey) && paramValue != null && !"".equals(paramValue)) {
                // 键不是sign,拼接到字符串
                sb.append(paramKey + "=" + paramValue + "&");
            }
        }
        // 拼接上API密钥得到签名所需字符串
        sb.append("key=" + merchantKey);
        // MD5加密
        return encode("MD5", sb.toString()).toUpperCase();
    }

    //Map转换为xml
    public static String mapToXML(Map<String, String> map) {
        // 定义StringBuffer
        StringBuffer sb = new StringBuffer("<xml>");
        // 遍历集合
        for (String key : map.keySet()) {
            sb.append("<" + key + "><![CDATA[" + map.get(key).toString() + "]]></" + key + ">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    //加密
    public static String encode(String algorithm, String str) throws Exception {
        // 定义MessageDigest对象，用于为应用程序提供信息摘要算法的功能,简单点说就是用于生成散列码
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 转化成utf-8编码
        messageDigest.update(str.getBytes("UTF-8"));
        // 把密文转化成16进制字符串
        return new HexBinaryAdapter().marshal(messageDigest.digest());
    }

    //XML转换为Map对象
    public static Map<String, String> XMLToMap(InputStream inputStream) throws Exception {
        // 定义sortedMap存储用来签名的参数
        SortedMap<String, String> resultMap = new TreeMap<String, String>();

        // DOM解析器工厂对象
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
        //禁止解析xml时访问外部实体(防止XXE漏洞,阻止xml实体攻击)
        builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        // DOM解析器对象
        DocumentBuilder builder = builderFactory.newDocumentBuilder();
        // 解析xml文件获取Document对象
        Document domDcoument = builder.parse(inputStream);
        // 创建DOMReader对象
        DOMReader reader = new DOMReader();
        // 获取dom4j document对象
        org.dom4j.Document document = reader.read(domDcoument);
        // 获取根节点
        org.dom4j.Element root = document.getRootElement();
        // 获取根节点下所子节点集合
        List<Element> eleList = root.elements();
        // 遍历根节点下的所有子节点集合
        for (org.dom4j.Element ele : eleList) {
            // 获取节点对应的参数名与参数值，并设置到SortedMap中
            resultMap.put(ele.getName(), ele.getTextTrim());
        }
        return resultMap;
    }

}
